package com.jiazixuan.stock.service.impl;

import com.google.common.collect.Lists;
import com.jiazixuan.stock.face.StockCacheFace;
import com.jiazixuan.stock.mapper.*;
import com.jiazixuan.stock.pojo.entity.StockBlockRtInfo;
import com.jiazixuan.stock.pojo.entity.StockMarketIndexInfo;
import com.jiazixuan.stock.pojo.entity.StockOuterMarketIndexInfo;
import com.jiazixuan.stock.pojo.entity.StockRtInfo;
import com.jiazixuan.stock.pojo.vo.StockInfoConfig;
import com.jiazixuan.stock.service.StockTimerTaskService;
import com.jiazixuan.stock.utils.DateTimeUtil;
import com.jiazixuan.stock.utils.IdWorker;
import com.jiazixuan.stock.utils.ParseType;
import com.jiazixuan.stock.utils.ParserStockInfoUtil;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
@Slf4j
public class StockTimerTaskServiceImpl implements StockTimerTaskService {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private StockInfoConfig stockInfoConfig;
    @Autowired
    private IdWorker idWorker;
    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;
    @Autowired
    private StockBusinessMapper stockBusinessMapper;
    //注入格式解析bean
    @Autowired
    private ParserStockInfoUtil parserStockInfoUtil;

    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;

    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    private StockOuterMarketIndexInfoMapper stockOuterMarketIndexInfoMapper;

    @Autowired
    private StockCacheFace stockCacheFace;
    /**
     * 必须保证该对象无状态（不对该对象进行修改操作）
     */
    private HttpEntity<Object> httpEntity;
    @Override
    public void getInnerMarketInfo() {
        //1.阶段1：采集原始数据
        //1.1 定义url地址
        String url = stockInfoConfig.getMarketUrl()+String.join(",",stockInfoConfig.getInner());
//        //1.2 维护请求头，添加防盗链和用户标识
//        HttpHeaders headers = new HttpHeaders();
//        //防盗链
//        headers.add("Referer","https://finance.sina.com.cn/stock/");
//        //用户客户端标识
//        headers.add("user-agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0");
//        //维护http请求实体对象
//        HttpEntity<Object> httpEntity = new HttpEntity<>(headers);
        //1.3 发送请求
        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
        //1.4 判断请求是否成功（查看状态码）
        int statusCodeValue = responseEntity.getStatusCodeValue();
        if (statusCodeValue!=200) {
            //当前请求失败
            log.error("当前时间点：{}，采集数据失败，http状态码：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),statusCodeValue);
            return;
        }
        //获取js格式数据
        String jsData = responseEntity.getBody();
        log.info("当前时间点：{}，采集原始数据：{}",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),jsData);
        //2.阶段2：java正则解析原始数据
        //var hq_str_sh000001="上证指数,3761.8801,3765.8759,3812.5143,3817.1589,3745.3091,0,0,626223380,979061710221,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2025-09-05,15:30:39,00,";
//        //2.1 定义正则表达式
//        String reg = "var hq_str_(.+)=\"(.+)\";";
//        //2.1 编译正则表达式
//        Pattern pattern = Pattern.compile(reg);
//        //2.3 匹配字符串
//        Matcher matcher = pattern.matcher(jsData);
//        //创建list集合接收大盘数据
//        ArrayList<StockMarketIndexInfo> entities = new ArrayList<>();
//        while (matcher.find()) {
//            //1.获取大盘的编码
//            String marketCode = matcher.group(1);
//            //2.获取其他信息
//            String otherInfo = matcher.group(2);
//            //将other字符串以逗号切割，获取大盘的详情信息
//            String[] splitArr = otherInfo.split(",");
//            //大盘名称
//            String marketName=splitArr[0];
//            //获取当前大盘的开盘点数
//            BigDecimal openPoint=new BigDecimal(splitArr[1]);
//            //前收盘点
//            BigDecimal preClosePoint=new BigDecimal(splitArr[2]);
//            //获取大盘的当前点数
//            BigDecimal curPoint=new BigDecimal(splitArr[3]);
//            //获取大盘最高点
//            BigDecimal maxPoint=new BigDecimal(splitArr[4]);
//            //获取大盘的最低点
//            BigDecimal minPoint=new BigDecimal(splitArr[5]);
//            //获取成交量
//            Long tradeAmt=Long.valueOf(splitArr[8]);
//            //获取成交金额
//            BigDecimal tradeVol=new BigDecimal(splitArr[9]);
//            //时间
//            Date curTime = DateTimeUtil.getDateTimeWithoutSecond(splitArr[30] + " " + splitArr[31]).toDate();
//            //3.阶段3：解析数据封装entity
//            // 组装entity对象
//            StockMarketIndexInfo entity = StockMarketIndexInfo.builder()
//                    .id(idWorker.nextId())
//                    .marketCode(marketCode)
//                    .marketName(marketName)
//                    .curPoint(curPoint)
//                    .openPoint(openPoint)
//                    .preClosePoint(preClosePoint)
//                    .maxPoint(maxPoint)
//                    .minPoint(minPoint)
//                    .tradeVolume(tradeVol)
//                    .tradeAmount(tradeAmt)
//                    .curTime(curTime)
//                    .build();
//            //收集封装的对象，方便批量插入
//            entities.add(entity);
//        }
        List<StockMarketIndexInfo> entities = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.INNER);
        log.info("解析大盘数据完毕");
        //4.阶段4：调用mybatis批量入库
        int count = stockMarketIndexInfoMapper.insertBatch(entities);
      //  int count = 2;
        if (count>0) {
            //通知后台终端刷新本地缓存，发送的日期数据是告知对方当前更新的股票数据所在时间点
            rabbitTemplate.convertAndSend("stockExchange","inner.market",new Date());
            log.info("当前时间：{}，插入大盘数据：{}成功",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),entities);
        }else{
            log.error("当前时间：{}，插入大盘数据：{}失败",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),entities);
        }
    }

    /**
     * 定义获取分钟级股票数据（个股）
     */
    @Override
    public void getStockRtIndex() {
        List<String> allCodes = stockCacheFace.getAllStockCodeWithPredix();
        //避免一次查询过多，分片查询
        long startTime = System.currentTimeMillis();
        Lists.partition(allCodes,15).forEach(codes->{
            //原始方案
            //分批采集
            //拼接url地址
//            String url = stockInfoConfig.getMarketUrl()+String.join(",",codes);
//            log.info("开始处理股票代码: {}", codes);
//            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
//            log.info("状态码：{}",responseEntity.getStatusCodeValue());
//            //判断状态码
//            int statusCodeValue = responseEntity.getStatusCodeValue();
//            if (statusCodeValue!=200) {
//                //当前请求失败
//                log.error("当前时间点：{}，采集数据失败，http状态码：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),statusCodeValue);
//                return;
//            }
//            //获取原始js数据
//            String jsData = responseEntity.getBody();
//            log.info("获取原始数据：{}",jsData);
//            //调用工具类解析数据
//            List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
//            log.info("采集个股数据：{}",list);
//            //批量插入,保存个股数据
//            int count = stockRtInfoMapper.insertBatch(list);
//            log.info("count大小：{}",count);
//            if (count>0) {
//                log.info("当前时间：{}，插入个股数据：{}成功",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),list);
//            }else{
//                log.error("当前时间：{}，插入个股数据：{}失败",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),list);
//            }
//        });
            //方案一：原始方案采集个股数据时将集合分开，然后分批次串行采集数据，效率不高，存在较高的采集延迟：引入线程
            //代码的问题：1.每次来任务，就创建一个线程，复用性差。2.如果每次多线程使用不当，造成cpu竞争激烈，导致频繁的上下文切换，导致程序性能降低
//        new Thread(()->{
//            String url = stockInfoConfig.getMarketUrl()+String.join(",",codes);
//            log.info("开始处理股票代码: {}", codes);
//            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
//            log.info("状态码：{}",responseEntity.getStatusCodeValue());
//            //判断状态码
//            int statusCodeValue = responseEntity.getStatusCodeValue();
//            if (statusCodeValue!=200) {
//                //当前请求失败
//                log.error("当前时间点：{}，采集数据失败，http状态码：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),statusCodeValue);
//                return;
//            }
//            //获取原始js数据
//            String jsData = responseEntity.getBody();
//            log.info("获取原始数据：{}",jsData);
//            //调用工具类解析数据
//            List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
//            log.info("采集个股数据：{}",list);
//            //批量插入,保存个股数据
//            int count = stockRtInfoMapper.insertBatch(list);
//            log.info("count大小：{}",count);
//            if (count>0) {
//                log.info("当前时间：{}，插入个股数据：{}成功",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),list);
//            }else{
//                log.error("当前时间：{}，插入个股数据：{}失败",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),list);
//            }
//        }).start();
            threadPoolTaskExecutor.execute(()->{
            String url = stockInfoConfig.getMarketUrl()+String.join(",",codes);
            ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
            //判断状态码
            int statusCodeValue = responseEntity.getStatusCodeValue();
            if (statusCodeValue!=200) {
                //当前请求失败
                log.error("当前时间点：{}，采集数据失败，http状态码：{}", DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),statusCodeValue);
                return;
            }
            //获取原始js数据
            String jsData = responseEntity.getBody();
            //调用工具类解析数据
            List<StockRtInfo> list = parserStockInfoUtil.parser4StockOrMarketInfo(jsData, ParseType.ASHARE);
            log.info("采集个股数据：{}",list);
            //批量插入,保存个股数据
            int count = stockRtInfoMapper.insertBatch(list);
            log.info("count大小：{}",count);
            if (count>0) {
                log.info("当前时间：{}，插入个股数据：{}成功",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),list);
            }else{
                log.error("当前时间：{}，插入个股数据：{}失败",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),list);
            }
            });
        });
        long takeTime = System.currentTimeMillis()- startTime;
        log.info("花费的时间：{}ms",takeTime);
    }

    @Override
    public void getStockSectorRtIndex() {
        //定义url地址
        String url = stockInfoConfig.getBlockUrl();
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
        //获取js原始数据
        String result = exchange.getBody();
        //解析数据
        List<StockBlockRtInfo> infos = parserStockInfoUtil.parse4StockBlock(result);
        log.info("板块数据量：{}",infos.size());
        int count = stockBlockRtInfoMapper.insertBatch(infos);
        if (count>0) {
            log.info("当前时间：{}，插入数据：{}成功",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),infos);
        }else{
            log.error("当前时间：{}，插入数据：{}失败",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),infos);
        }
    }

    @Override
    public void getStockOuterRtIndex() {
        //定义url地址
        String url = stockInfoConfig.getMarketUrl()+String.join(",",stockInfoConfig.getOuter());
        ResponseEntity<String> exchange = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
        //获取js原始数据
        String body = exchange.getBody();
        //解析数据
        List<StockOuterMarketIndexInfo> infos = parserStockInfoUtil.parser4StockOrMarketInfo(body, ParseType.OUTER);
        //向数据库插入数据
        int count = stockOuterMarketIndexInfoMapper.insertBatch(infos);
        if (count>0) {
            log.info("当前时间：{}，插入外盘数据：{}成功",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),infos);
        }else{
            log.error("当前时间：{}，插入外盘数据：{}失败",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),infos);
        }
    }

    /**
     * bean生命周期的初始化回调方法
     */
    @PostConstruct
    public void initData(){
        //维护请求头
        HttpHeaders headers = new HttpHeaders();
        //防盗链
        headers.add("Referer","https://finance.sina.com.cn/stock/");
        //用户客户端标识
        headers.add("user-agent","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0");
        //维护http请求实体对象
        httpEntity = new HttpEntity<>(headers);
    }
}
